﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Data;

using Ict.DataAccess.Attributes;
using System.Globalization;

namespace Ict.DataAccess.Linq
{
    public class SelectableProvider<T> : ExecutableProvider, IQueryProvider
    {

        public SelectableProvider()
        {
        }

        #region IQueryProvider 成员

        public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
        {
            // 分解表达式，该表达式一定是一个第一参数为Selectable的Method Call，以下称该参数为源查询
            var exp = expression as MethodCallExpression;
            var query = (exp.Arguments[0] as ConstantExpression).Value as Selectable<T>;

            string methodName = exp.Method.Name;

            if (methodName == "Where")
            {
                // 如果表达式是Where或OrderBy，或其它方法，则增加至当前查询的表达式集合。
                query.WhereClause.Add(((exp.Arguments[1] as UnaryExpression).Operand as LambdaExpression).Body);
            }
            else if (methodName.StartsWith("OrderBy") || methodName.StartsWith("ThenBy"))
            {
                // 如果表达式是Where或OrderBy，或其它方法，则增加至当前查询的表达式集合。
                query.OrderClause.Add(expression);
            }
            else if (methodName == "Take")
            {
                query.TakeCount = (int)(exp.Arguments[1] as ConstantExpression).Value;
            }
            else if (methodName == "Join")
            {
                // 如果当前表达式为SelectMany或者Join，则生成新查询，
                // 提取第二个参数所指查询作为第二查询，如果该查询表映射为空，则为其创建表映射。
                // 将源查询和作为子查询加入新查询，如果是Join，则增加表连接条件。
                SelectableJoin<TElement> newquery = new SelectableJoin<TElement>(query,
                    (exp.Arguments[1] as ConstantExpression).Value as SelectableBase,
                    ((exp.Arguments[2] as UnaryExpression).Operand as LambdaExpression).Body,
                    ((exp.Arguments[3] as UnaryExpression).Operand as LambdaExpression).Body, 
                    ((exp.Arguments[4] as UnaryExpression).Operand as LambdaExpression));
                return newquery;
            }
            else if (methodName == "SelectMany")
            {
                // 提取第二个参数所指查询作为第二查询，如果该查询表映射为空，则为其创建表映射。
                // 将源查询和作为子查询加入新查询，如果是Join，则增加表连接条件。
                ParameterExpression param2 = ((exp.Arguments[2] as UnaryExpression).Operand as LambdaExpression).Parameters[1];
                SelectableBase query2 = SelectableBase.Create(param2.Type);
                SelectableMany<TElement> newquery = new SelectableMany<TElement>(query,
                    query2,
                    ((exp.Arguments[2] as UnaryExpression).Operand as LambdaExpression));
                return newquery;
            }
            else if (methodName == "Select")
            {
                // 如果表达式是Select则设置查询的结果转换函数
                if (typeof(TElement) != typeof(T))
                {
                    SelectableNew<TElement> newquery = new SelectableNew<TElement>(
                        query, ((exp.Arguments[1] as UnaryExpression).Operand as LambdaExpression));
                    return newquery;
                }
            }
            else
                throw new NotSupportedException(string.Format("不支持的方法调用：{0}", methodName));

            return (IQueryable<TElement>)query;
        }

        public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
        {           
            return (IQueryable)CreateQuery<T>(expression);
        }

        public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
        {
            List<T> resultList = new List<T>();

            string executeMethod = null;
            Selectable<T> query = null;

            if (expression is MethodCallExpression)
            {
                MethodCallExpression methodCall = (MethodCallExpression)expression;
                executeMethod = methodCall.Method.Name;
                expression = methodCall.Arguments[0];
                query = (expression as ConstantExpression).Value as Selectable<T>;

                // 因为性能原因暂时屏蔽
                if (executeMethod == "First" || executeMethod == "FirstOrDefault")
                {
                    //Expression<Func<Selectable<T>, IQueryable<T>>> takeFirstExp = p => p.Take(1);
                    //((expression as ConstantExpression).Value as Selectable<T>).Expressions.Add(takeFirstExp.Body);
                    query.TakeCount = 1;
                }
                else if (executeMethod == "Count")
                {
                    query.IsCountOnly = true;
                }
                else
                    throw new NotSupportedException("Method Call is not supported. Unable to generate SQL statement.");
            }
            else
            {
                query = (expression as ConstantExpression).Value as Selectable<T>;
            }
            string sql = BuildSql(query);

            Debug.WriteLine(sql);

            using (this.Executor)
            {
                using (IDataReader reader = Executor.ExecuteReader(sql))
                {
                    try
                    {
                        ExtractData(reader, resultList);
                        reader.Close();
                    }
                    finally
                    {
                        reader.Close();
                    }
                }
            }

            if (executeMethod == null)
                return (TResult)(IEnumerator)resultList.GetEnumerator();
            else //if (executeMethod == "First" || executeMethod == "FirstOrDefault")
            {
                if (resultList.Count > 0)
                {
                    return (TResult)(object)resultList.First();
                }
                else
                    return default(TResult);
            }
            //else
            //    throw new NotSupportedException();
        }

        #endregion

        private object ConvertField(object fieldValue, Type type)
        {
            if (fieldValue != null && !Convert.IsDBNull(fieldValue))
            {
                if (type == typeof(string))
                {
                    return fieldValue.ToString().TrimEnd();
                }
                else
                {
                    if (type.IsEnum)
                    {
                        return Enum.ToObject(type, fieldValue);
                    }
                    if (typeof(IConvertible).IsAssignableFrom(type))
                    {
                        try
                        {
                            return Convert.ChangeType(fieldValue, type);
                        }
                        catch
                        {
                        }
                    }
                    else
                    {
                        try
                        {
                            return Activator.CreateInstance(type, fieldValue);
                        }
                        catch
                        {
                        }
                    }
                }
            }
            // 返回默认值或空值
            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }

        private void ExtractData(IDataReader reader, List<T> resultList)
        {
            Type entityType = typeof(T);
            T entity = default(T);

            while (reader.Read())
            {
                if (entityType == typeof(System.String) || entityType.IsValueType)
                {
                    entity = (T)ConvertField(reader[0], entityType);
                }
                else
                {
                    entity = (T)Activator.CreateInstance(entityType);

                    for (int i = 0; i<reader.FieldCount; i++)
                    {
                        string fieldName = reader.GetName(i);
                        PropertyInfo propertyInfo = null;
                        try
                        {
                            propertyInfo = entityType.GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance);
                        }
                        catch (AmbiguousMatchException)
                        {
                            // 应付有属性在子类和基类中重复定义的情况
                            propertyInfo = entityType.GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                        }
                        if (propertyInfo != null)
                        {
                            object val = ConvertField(reader[i], propertyInfo.PropertyType);
                            propertyInfo.SetValue(entity, val, null);
                        }
                    }
                    
                    //PropertyInfo[] properties = entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                    
                    //foreach (PropertyInfo propertyInfo in properties)
                    //{
                    //    var nonfield = propertyInfo.GetCustomAttributes(typeof(NonFieldAttribute), true);
                    //    if (nonfield.Length == 0)
                    //    {
                            

                    //        if (val is string)
                    //        {
                    //            if (propertyInfo.DeclaringType.Equals(typeof(DateTime))
                    //                || propertyInfo.DeclaringType.Equals(typeof(DateTime?)))
                    //            {
                    //                DateTime date;
                    //                DateTime.TryParse(val as string, out date);
                    //                val = date;
                    //            }
                    //            propertyInfo.SetValue(entity, ((string)val).TrimEnd(), null);
                    //        }
                    //        else
                    //            propertyInfo.SetValue(entity, val, null);
                    //        i++;
                    //    }
                    //}
                }
                resultList.Add(entity);
            }
        }

        private object GetDBNullValue(Type type)
        {
 
            if (type == typeof(int))
                return (int)0;
            else if (type == typeof(decimal))
                return (decimal)0;
            else if (type == typeof(bool))
                return false;
            else
                return null;
        }

        public object Execute(System.Linq.Expressions.Expression expression)
        {
            return this.Execute<T>(expression);
        }

        private string BuildSql(Selectable<T> query)
        {
            StringBuilder stringBuilder = new StringBuilder();

            query.BuildSql(stringBuilder);

            return stringBuilder.ToString();
        }
    }
}
